
using System;
using System.Configuration;
using System.Web;
using System.Collections.Generic;
using ScrewTurn.Wiki.PluginFramework;

namespace ScrewTurn.Wiki {

    /// <summary>
    /// Contains the Contents.
    /// </summary>
    public static class Content {

		// Contains cached Pages
		private static Dictionary<PageInfo, PageContent> cache = new Dictionary<PageInfo, PageContent>(Settings.CacheSize); // Used as lock target for pages cache
		private static Dictionary<PageInfo, string> formattedCache = new Dictionary<PageInfo, string>(Settings.CacheSize);
		private static List<PageInfo> addedKeys = new List<PageInfo>(Settings.CacheSize);

		// Contains cached text such as header, sidebar, etc.
		private static Dictionary<string, string> pseudoCache = new Dictionary<string, string>(); // Used as lock target for itself

		/// <summary>
		/// Gets or sets the Pseudo Cache object.
		/// </summary>
		public static Dictionary<string, string> PseudoCache {
			get {
				lock(pseudoCache) {
					// If cache is disabled, always return an empty instance (quick-and-dirty)
					if(Settings.DisableCache) return new Dictionary<string, string>();
					else return pseudoCache;
				}
			}
		}

        /// <summary>
        /// Reads the Content of a Page.
        /// </summary>
        /// <param name="pageInfo">The Page.</param>
        /// <param name="cached">Specifies whether the page has to be cached or not.</param>
        /// <returns>The Page Content.</returns>
        public static PageContent GetPageContent(PageInfo pageInfo, bool cached) {
			lock(cache) {
				PageContent result;
				if(!cache.TryGetValue(pageInfo, out result)) {
					result = pageInfo.Provider.GetContent(pageInfo);
					if(cached && !pageInfo.NonCached && !Settings.DisableCache) {
						cache.Add(pageInfo, result);
						addedKeys.Add(pageInfo);
					}
					if(cache.Count > Settings.CacheSize) {
						CutCache();
					}
				}
				else {
					// This way the least-recently used Pages stay at the beginning of the array,
					// while the most-recently used Pages stay at the end
					addedKeys.Remove(pageInfo);
					addedKeys.Add(pageInfo);
				}

				// result should NEVER be null, so if it happens throw exception
				if(result == null) throw new Exception("Cannot extract Page Content for " + (pageInfo != null ? pageInfo.Name : "a null page") + ".");

				return result;
			}
        }

		/// <summary>
		/// Gets the formatted Page Content, properly handling content caching and the Formatting Pipeline.
		/// </summary>
		/// <param name="page">The Page to get the formatted Content of.</param>
		/// <param name="cached">Specifies whether the formatted content has to be cached or not.</param>
		/// <param name="isInContext">Specifies whether the content should be retrieved using the page context.</param>
		/// <returns>The formatted content.</returns>
		public static string GetFormattedPageContent(PageInfo page, bool cached, bool isInContext) {
			lock(cache) {
				string content;
				if(!formattedCache.TryGetValue(page, out content)) {
					PageContent pg = GetPageContent(page, cached);
					PageInfo[] linkedPages;
					content = FormattingPipeline.FormatWithPhase1And2(pg.Content, page, out linkedPages);
					pg.LinkedPages = linkedPages;
					if(cached && !page.NonCached && !Settings.DisableCache && isInContext) formattedCache.Add(page, content);
				}
				return FormattingPipeline.FormatWithPhase3(content, isInContext ? page : null);
			}
		}

		/// <summary>
		/// Gets the formatted Page Content, properly handling content caching and the Formatting Pipeline.
		/// </summary>
		/// <param name="page">The Page to get the formatted Content of.</param>
		/// <param name="cached">Specifies whether the formatted content has to be cached or not.</param>
		/// <returns>The formatted content.</returns>
		public static string GetFormattedPageContent(PageInfo page, bool cached) {
			return GetFormattedPageContent(page, cached, true);
		}

		/// <summary>
		/// Invalidates the cached Content of a Page.
		/// </summary>
		/// <param name="pageInfo">The Page to invalidate the cached content of.</param>
        public static void Invalidate(PageInfo pageInfo) {
			lock(cache) {
				if(cache == null) return;
				cache.Remove(pageInfo);
				formattedCache.Remove(pageInfo);
				addedKeys.Remove(pageInfo);
			}
        }

		/// <summary>
		/// Invalidates all the cache Contents.
		/// </summary>
		public static void InvalidateAll() {
			lock(cache) {
				cache.Clear();
				formattedCache.Clear();
				addedKeys.Clear();
			}
		}

        private static void CutCache() {
			lock(cache) {
				// Clear older elements in the cache
				int cutSize = Settings.CacheCutSize;
				for(int i = 0; i < cutSize; i++) {
					if(cache.Count == 0 || cache == null) break;
					cache.Remove(addedKeys[0]);
					formattedCache.Remove(addedKeys[0]);
					addedKeys.Remove(addedKeys[0]);
				}
			}
        }

        /// <summary>
        /// Gets the # of Pages in the Cache.
        /// </summary>
        public static int CacheUsage {
            get {
				lock(cache) {
					if(cache == null) return 0;
					return cache.Count;
				}
            }
        }

    }

}
